package edu.del.teamknowledgebase.service.auth.impl;
import java.io.IOException;
import java.util.Date;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import edu.del.teamknowledgebase.common.constant.cache.SessionConstant;
import edu.del.teamknowledgebase.common.exception.BusinessException;
import edu.del.teamknowledgebase.common.result.BusinessCode;
import edu.del.teamknowledgebase.common.session.SessionData;
import edu.del.teamknowledgebase.entity.User;
import edu.del.teamknowledgebase.service.UserService;
import edu.del.teamknowledgebase.service.auth.AuthService;
import edu.del.teamknowledgebase.service.auth.CaptchaService;
import edu.del.teamknowledgebase.service.email.EmailService;
import edu.del.teamknowledgebase.service.sms.SmsService;
import edu.del.teamknowledgebase.utils.BCryptUtil;
import edu.del.teamknowledgebase.utils.FileUploadUtil;
import edu.del.teamknowledgebase.utils.ModuleIdGenerator;
import edu.del.teamknowledgebase.dto.LoginRequest;
import edu.del.teamknowledgebase.dto.RegisterRequest;
import edu.del.teamknowledgebase.vo.UserVO;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.Objects;

@Service
@Transactional
@RequiredArgsConstructor
@Slf4j
public class AuthServiceImpl implements AuthService {

    private final UserService userService;

    private final CaptchaService captchaService;

    private final EmailService emailService;

    private final SmsService smsService;

    private final ModuleIdGenerator moduleIdGenerator;

    @Override
    public UserVO login(LoginRequest loginRequest, HttpServletRequest request) {
        String account = loginRequest.getAccount();
        String password = loginRequest.getPassword();
        String loginType = loginRequest.getLoginType();
        String captcha = loginRequest.getCaptcha();
        String captchaKey = loginRequest.getCaptchaKey();

        // 1. 参数校验
        if (StrUtil.isBlank(account)) {
            throw new BusinessException(BusinessCode.PHONE_OR_EMAIL_EMPTY, "手机号或邮箱不能为空");
        }
        if (StrUtil.isBlank(password)) {
            throw new BusinessException(BusinessCode.PASSWORD_EMPTY, "密码不能为空");
        }
        if (StrUtil.isBlank(loginType)) {
            throw new BusinessException(BusinessCode.LOGIN_TYPE_EMPTY, "登录方式不能为空");
        }
        if (StrUtil.isBlank(captcha) || StrUtil.isBlank(captchaKey)) {
            throw new BusinessException(BusinessCode.CAPTCHA_EMPTY, "验证码不能为空");
        }

        // 2. 验证图片验证码
        boolean captchaValid = captchaService.verifyCaptcha(captchaKey, captcha);
        if (!captchaValid) {
            throw new BusinessException(BusinessCode.CAPTCHA_ERROR, "验证码错误");
        }

        // 3. 根据登录类型查询用户
        User user = null;
        switch (loginType) {
            case "email":
                if (!account.matches("^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$")) {
                    throw new BusinessException(BusinessCode.EMAIL_FORMAT_ERROR, "邮箱格式不正确");
                }
                user = userService.getOne(new QueryWrapper<User>().eq("email", account));
                if (user == null) {
                    throw new BusinessException(BusinessCode.USER_NOT_EXIST, "用户不存在");
                }
                break;
            case "phone":
                if (!account.matches("^1[3-9]\\d{9}$")) {
                    throw new BusinessException(BusinessCode.PHONE_FORMAT_ERROR, "手机号格式错误");
                }
                user = userService.getOne(new QueryWrapper<User>().eq("phone", account));
                if (user == null) {
                    throw new BusinessException(BusinessCode.USER_NOT_EXIST, "用户不存在");
                }
                break;
            default:
                throw new BusinessException(BusinessCode.LOGIN_TYPE_ERROR, "登录方式错误");
        }

        // 4. 验证密码
        if (!BCryptUtil.checkPassword(password, user.getPassword())) {
            throw new BusinessException(BusinessCode.PASSWORD_ERROR, "密码错误");
        }

        // 5. 检查用户状态
        if (user.getIsDeleted() == 1) {
            throw new BusinessException(BusinessCode.USER_DISABLED, "账号已被禁用，请联系管理员");
        }

        // 6. 创建 Session（Spring Session 会自动持久化到 Redis）
        HttpSession session = request.getSession(true);
        String sessionId = session.getId();

        // 7. 构建并保存 Session 数据
        SessionData sessionData = new SessionData();
        sessionData.setUserId(user.getId());
        sessionData.setUsername(user.getUsername());
        sessionData.setRole(user.getRole());
        sessionData.setLoginTime(LocalDateTime.now());
        sessionData.setLastActiveTime(LocalDateTime.now());
        sessionData.setLoginIp(getClientIp(request));

        // 8. 保存到 HttpSession（Spring Session 会自动同步到 Redis）
        session.setAttribute(SessionConstant.USER_INFO_KEY, sessionData);

        // 9. 返回脱敏后的用户信息
        UserVO userInfo = UserVO.fromUser(user, true);
        log.info("用户登录成功: userId={}, username={}, loginType={}, sessionId={}, ip={}",
                user.getId(), user.getUsername(), loginType, sessionId, getClientIp(request));
        return userInfo;
    }

    @Override
    public UserVO register(RegisterRequest registerRequest, HttpServletRequest request) {
        String phone = registerRequest.getPhone();
        String password = registerRequest.getPassword();
        String confirmPassword = registerRequest.getConfirmPassword();
        String email = registerRequest.getEmail();
        String code = registerRequest.getCode();

        // 1. 参数校验
        if (StrUtil.isBlank(phone)) {
            throw new BusinessException(BusinessCode.PHONE_EMPTY,"手机号不能为空");
        }
        if (!phone.matches("^1[3-9]\\d{9}$")) {
            throw new BusinessException(BusinessCode.PHONE_FORMAT_ERROR,"手机号格式错误");
        }
        if (StrUtil.isBlank(password)) {
            throw new BusinessException(BusinessCode.PASSWORD_EMPTY,"密码不能为空");
        }
        if (password.length() < 6 || password.length() > 20) {
            throw new BusinessException(BusinessCode.PASSWORD_LENGTH_ERROR,"密码长度必须在6-20位之间");
        }
        if (!password.equals(confirmPassword)) {
            throw new BusinessException(BusinessCode.PASSWORD_NOT_EQUAL,"两次密码输入不一致");
        }
        if (StrUtil.isBlank(code)) {
            throw new BusinessException(BusinessCode.CODE_EMPTY,"验证码不能为空");
        }

        // 2. 验证手机验证码
        boolean verificationCode = smsService.verifyCode(phone, code);
        if (!verificationCode) {
            throw new BusinessException(BusinessCode.CODE_ERROR,"验证码错误或已过期");
        }

        // 3. 检查手机号是否已注册
        QueryWrapper<User> qw = new QueryWrapper<User>().eq("phone", phone);
        User existUser = userService.getOne(qw);
        if (existUser != null) {
            throw new BusinessException(BusinessCode.PHONE_EXIST,"手机号已存在");
        }

        // 4. 检查邮箱是否已注册（如果填写了邮箱）
        if (StrUtil.isNotBlank(email)) {
            if (!email.matches("^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$")) {
                throw new BusinessException(BusinessCode.EMAIL_FORMAT_ERROR,"邮箱格式错误");
            }

            qw = new QueryWrapper<User>().eq("email", email);
            User emailUser = userService.getOne(qw);

            if (emailUser != null) {
                throw new BusinessException(BusinessCode.EMAIL_EXIST,"邮箱已存在");
            }
        }

        // 5. 对密码进行加密
        String hashPassword = BCryptUtil.hashPassword(password);

        // 6. 构建随机用户名
        String username = moduleIdGenerator.generateModuleId("user");

        // 7. 创建用户
        User user = new User();
        user.setUsername(username);
        user.setPassword(hashPassword);
        user.setEmail(email);
        user.setAvatar("");
        user.setRole("USER");
        user.setPhone(phone);
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        user.setIsDeleted(0);

        // 8. 保存用户
        boolean save = userService.save(user);
        if (!save) {
            throw new BusinessException(BusinessCode.REGISTER_FAILED,"注册失败");
        }
        log.info("用户注册成功: ip={}", getClientIp(request));

        return UserVO.fromUser(user, true);
    }

    @Override
    public void logout(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            String sessionId = session.getId();
            SessionData sessionData = (SessionData) session.getAttribute(SessionConstant.USER_INFO_KEY);

            // 销毁 Session（Spring Session 会自动从 Redis 删除）
            session.invalidate();

            if (sessionData != null) {
                log.info("用户登出成功: userId={}, username={}, sessionId={}",
                        sessionData.getUserId(), sessionData.getUsername(), sessionId);
            } else {
                log.info("用户登出成功: sessionId={}", sessionId);
            }
        }
    }

    @Override
    public UserVO getCurrentUser(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session == null) {
            throw new BusinessException(BusinessCode.NOT_LOGIN, "未登录");
        }

        SessionData sessionData = (SessionData) session.getAttribute(SessionConstant.USER_INFO_KEY);
        if (sessionData == null) {
            throw new BusinessException(BusinessCode.SESSION_EXPIRED, "登录已过期，请重新登录");
        }

        // 根据 userId 查询完整用户信息
        User user = userService.getOne(new QueryWrapper<User>().eq("id", sessionData.getUserId()));
        if (user == null) {
            throw new BusinessException(BusinessCode.USER_NOT_EXIST, "用户不存在");
        }

        return UserVO.fromUser(user, true);
    }

    @Override
    public boolean isLoggedIn(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session == null) {
            return false;
        }

        SessionData sessionData = (SessionData) session.getAttribute(SessionConstant.USER_INFO_KEY);
        return sessionData != null;
    }

    @Override
    public UserVO getSensitiveInfo(HttpServletRequest request, String password) {
        HttpSession session = request.getSession(false);
        if (session == null) {
            throw new BusinessException(BusinessCode.NOT_LOGIN, "未登录");
        }

        SessionData sessionData = (SessionData) session.getAttribute(SessionConstant.USER_INFO_KEY);
        if (sessionData == null) {
            throw new BusinessException(BusinessCode.SESSION_EXPIRED, "登录已过期，请重新登录");
        }

        User user = userService.getOne(new QueryWrapper<User>().eq("id", sessionData.getUserId()));
        if (user == null) {
            throw new BusinessException(BusinessCode.USER_NOT_EXIST, "用户不存在");
        }

        if (StrUtil.isBlank(password)) {
            throw new BusinessException(BusinessCode.PASSWORD_EMPTY, "请输入密码");
        }

        if (!BCryptUtil.checkPassword(password, user.getPassword())) {
            throw new BusinessException(BusinessCode.PASSWORD_ERROR, "密码错误");
        }

        log.info("用户查看敏感信息: userId={}, ip={}", user.getId(), getClientIp(request));
        return UserVO.fromUser(user, false);
    }

    @Override
    public String uploadAvatar(MultipartFile file, HttpServletRequest request) {
        // 1. 验证用户是否登录
        HttpSession session = request.getSession(false);
        if (session == null) {
            throw new BusinessException(BusinessCode.NOT_LOGIN,"未登录");
        }

        SessionData sessionData = (SessionData) session.getAttribute(SessionConstant.USER_INFO_KEY);

        if (sessionData == null) {
            throw new BusinessException(BusinessCode.SESSION_EXPIRED, "登录已过期，请重新登录");
        }

        // 2. 验证文件
        if (file == null || file.isEmpty()) {
            throw new BusinessException(BusinessCode.FILE_EMPTY, "请选择文件");
        }

        // 3. 验证文件类型
        String contentType = file.getContentType();
        if (contentType == null || (!contentType.equals("image/jpeg")
                && !contentType.equals("image/png")
                && !contentType.equals("image/jpg"))) {
            throw new BusinessException(BusinessCode.FILE_TYPE_ERROR, "只支持 JPG、PNG 格式的图片");
        }

        // 4. 验证文件大小
        long maxSize = 5 * 1024 * 1024;
        if (file.getSize() > maxSize) {
            throw new BusinessException(BusinessCode.FILE_SIZE_ERROR, "文件大小不能超过 5MB");
        }

        try {
            // 5. 上传文件
            String avatarPath = FileUploadUtil.uploadAvatar(file);

            // 6. 获取用户信息
            QueryWrapper<User> qw = new QueryWrapper<User>().eq("id", sessionData.getUserId());
            User user = userService.getOne(qw);

            if (user == null) {
                throw new BusinessException(BusinessCode.USER_NOT_EXIST,"用户不存在");
            }

            // 7. 删除旧头像（如果存在）
            String oldAvatar = user.getAvatar();
            if (oldAvatar != null && !oldAvatar.isEmpty()) {
                FileUploadUtil.deleteAvatar(oldAvatar);
            }

            // 8. 更新用户头像
            user.setAvatar(avatarPath);
            boolean update = userService.updateById(user);
            if (!update) {
                throw new BusinessException(BusinessCode.USER_UPDATE_ERROR, "更新头像失败，请重试");
            }
            log.info("用户头像上传成功: userId={}, path={}, ip={}",
                    sessionData.getUserId(), avatarPath, getClientIp(request));
            return avatarPath;
        } catch (IOException e) {
            log.error("文件上传失败: userId={}, error={}", sessionData.getUserId(), e.getMessage());
            throw new BusinessException(BusinessCode.FILE_UPLOAD_ERROR, "文件上传失败，请重试");
        }
    }

    @Override
    public UserVO updateUsername(String username, HttpServletRequest request) {
        // 1. 验证用户是否登录
        HttpSession session = request.getSession(false);
        if (session == null) {
            throw new BusinessException(BusinessCode.NOT_LOGIN, "未登录");
        }

        SessionData sessionData = (SessionData) session.getAttribute(SessionConstant.USER_INFO_KEY);
        if (sessionData == null) {
            throw new BusinessException(BusinessCode.SESSION_EXPIRED, "登录已过期，请重新登录");
        }

        // 2. 参数校验
        if (StrUtil.isBlank(username)) {
            throw new BusinessException(BusinessCode.PARAM_VALID_ERROR, "用户名不能为空");
        }

        // 用户名长度校验（2 - 20个字符）
        if (username.length() < 2 || username.length() > 20) {
            throw new BusinessException(BusinessCode.PARAM_VALID_ERROR, "用户名长度必须在2-20个字符之间");
        }

        // 3. 查询当前用户
        User user = userService.getById(sessionData.getUserId());
        if (user == null) {
            throw new BusinessException(BusinessCode.USER_NOT_EXIST, "用户不存在");
        }

        // 4. 检查用户名是否与当前相同
        if (username.equals(user.getUsername())) {
            throw new BusinessException(BusinessCode.PARAM_VALID_ERROR, "新用户名与当前用户名相同");
        }

        // 5. 检查当前用户名是否被占用
        if (userService.getOne(new QueryWrapper<User>().eq("username", username)) != null) {
            throw new BusinessException(BusinessCode.USERNAME_EXISTS, "用户名已被占用");
        }

        // 6. 检查修改时间限制（30天内只能修改一次）
        Date lastUsernameUpdateTime = user.getLastUsernameUpdateTime();
        if (lastUsernameUpdateTime != null) {
            long now = System.currentTimeMillis();
            long diff = now - lastUsernameUpdateTime.getTime();
            long thirtyDaysInMillis = 30L * 24 * 60 * 60 * 1000;
            if (diff < thirtyDaysInMillis) {
                long remainingDays = (thirtyDaysInMillis - diff) / (24 * 60 * 60 * 1000);
                throw new BusinessException(BusinessCode.OPERATION_TOO_FREQUENT,
                        "用户名每 30 天只能修改一次，距离下次修改还需 " + remainingDays + " 天");
            }
        }

        // 7. 更新用户名和修改时间
        user.setUsername(username);
        user.setLastUsernameUpdateTime(new Date());
        user.setUpdateTime(new Date(System.currentTimeMillis()));
        boolean update = userService.updateById(user);
        if (!update) {
            throw new BusinessException(BusinessCode.USER_UPDATE_ERROR, "用户名修改失败，请重试");
        }

        // 8. 更新session
        sessionData.setUsername(username);
        session.setAttribute(SessionConstant.USER_INFO_KEY, sessionData);

        // 9. 返回更新后的用户信息
        log.info("用户修改用户名成功: userId={}, oldUsername={}, newUsername={}, ip={}",
                user.getId(), user.getUsername(), username, getClientIp(request));

        return UserVO.fromUser(user, true);
    }

    @Override
    public UserVO rebindPhone(String newPhone, String code, HttpServletRequest request) {
        // 1. 验证登录
        HttpSession session = request.getSession(false);
        if (session == null) {
            throw new BusinessException(BusinessCode.NOT_LOGIN, "未登录");
        }
        SessionData sessionData = (SessionData) session.getAttribute(SessionConstant.USER_INFO_KEY);
        if (sessionData == null) {
            throw new BusinessException(BusinessCode.SESSION_EXPIRED, "登录已过期，请重新登录");
        }

        // 2. 参数校验
        if (StrUtil.isBlank(newPhone)) {
            throw new BusinessException(BusinessCode.PHONE_EMPTY, "手机号不能为空");
        }

        if (!newPhone.matches("^1[3-9]\\d{9}$")) {
            throw new BusinessException(BusinessCode.PHONE_FORMAT_ERROR, "手机号格式错误");
        }

        // 3. 验证验证码
        boolean verifyCode = smsService.verifyCode(newPhone, code);
        if (!verifyCode) {
            throw new BusinessException(BusinessCode.CODE_ERROR, "验证码错误或已过期");
        }

        // 4. 查询当前用户
        User user = userService.getById(sessionData.getUserId());
        if (user == null) {
            throw new BusinessException(BusinessCode.USER_NOT_EXIST, "用户不存在");
        }

        // 5. 检查手机号是否与当前相同
        if (!Objects.equals(user.getPhone(), newPhone)) {
            boolean phoneExists = userService.count(new QueryWrapper<User>()
                    .eq("phone", newPhone)) > 0;
            if (phoneExists) {
                throw new BusinessException(BusinessCode.PHONE_EXIST, "手机号已被占用");
            }
        }

        // 6. 更新手机号
        user.setPhone(newPhone);
        user.setUpdateTime(new Date(System.currentTimeMillis()));
        boolean update = userService.updateById(user);
        if (!update) {
            throw new BusinessException(BusinessCode.USER_UPDATE_ERROR, "换绑失败");
        }

        // 这里不需要更新Session因为没有存储敏感信息
        log.info("用户换绑手机成功: userId={}, oldPhone{}, newPhone={}, ip={}", user.getId(), user.getPhone(), newPhone, getClientIp(request));

        return UserVO.fromUser(user, true);
    }

    @Override
    public UserVO rebindEmail(String newEmail, String code, HttpServletRequest request) {
        // 1. 验证登录
        HttpSession session = request.getSession(false);
        if (session == null) {
            throw new BusinessException(BusinessCode.NOT_LOGIN, "未登录");
        }

        SessionData sessionData = (SessionData) session.getAttribute(SessionConstant.USER_INFO_KEY);
        if (sessionData == null) {
            throw new BusinessException(BusinessCode.SESSION_EXPIRED, "登录已过期，请重新登录");
        }

        // 2. 参数校验
        if (StrUtil.isBlank(newEmail)) {
            throw new BusinessException(BusinessCode.EMAIL_EMPTY, "邮箱不能为空");
        }

        if (!newEmail.matches("^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$")) {
            throw new BusinessException(BusinessCode.EMAIL_FORMAT_ERROR, "邮箱格式错误");
        }

        //  3. 验证验证码
        boolean verified = emailService.verifyCode(newEmail, code);
        if (!verified) {
            throw new BusinessException(BusinessCode.CODE_ERROR, "验证码错误或已过期");
        }


        // 4. 查询当前用户
        User user = userService.getById(sessionData.getUserId());
        if (user == null) {
            throw new BusinessException(BusinessCode.USER_NOT_EXIST, "用户不存在");
        }

        // 5. 检查邮箱是否与当前相同
        if (!Objects.equals(user.getEmail(), newEmail)) {
            boolean emailExists = userService.count(new QueryWrapper<User>()
                    .eq("email", newEmail)) > 0;
            if (emailExists) {
                throw new BusinessException(BusinessCode.EMAIL_EXIST, "邮箱已被占用");
            }
        }

        // 6. 更新邮箱
        user.setEmail(newEmail);
        user.setUpdateTime(new Date(System.currentTimeMillis()));
        boolean update = userService.updateById(user);
        if (!update) {
            throw new BusinessException(BusinessCode.USER_UPDATE_ERROR, "换绑失败");
        }

        log.info("用户{}邮箱成功: userId={}, oldEmail={}, newEmail={}, ip={}",
                user.getEmail() == null ? "绑定" : "换绑",
                user.getId(), user.getEmail(), newEmail, getClientIp(request));

        return UserVO.fromUser(user, true);
    }

    /**
     * 获取客户端真实 IP
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

}
